home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 / Ham Radio 2000.iso / ham2000 / satellit / radserv / tty.c < prev    next >
C/C++ Source or Header  |  1995-01-23  |  13KB  |  458 lines

  1. /*
  2.  * %W% %E% %U%
  3.  *
  4.  * Routines for communication through an NT supported serial port (done
  5.  * "the right way"). These routines can be used to develop code for
  6.  * talking to a radio (or rotator with rs232 interface, etc.)
  7.  */
  8.  
  9. #include "radextrn.h"
  10. #include "vsttype.h"
  11.  
  12. BOOL
  13. TTYOpen(tty_t *tp )
  14. {
  15.     char       portname[24];
  16.     BOOL       result;
  17.     COMMTIMEOUTS  CommTimeOuts ;
  18.     extern BOOL TTYSetup(tty_t *);
  19.  
  20.     sprintf(portname, "\\\\.\\COM%d", tp->tt_portno);
  21.  
  22.     tp->tt_ttyh = CreateFile( portname, GENERIC_READ | GENERIC_WRITE,
  23.         0 /* Exclusive Access */,  NULL,
  24.         OPEN_EXISTING,  FILE_ATTRIBUTE_NORMAL | 
  25.         FILE_FLAG_OVERLAPPED, NULL);
  26.  
  27.     if (tp->tt_ttyh == INVALID_HANDLE_VALUE) {
  28.         sprintf(tmpbuf,"Cannot open COM%d - error %d\n", tp->tt_portno, GetLastError());
  29.         usermsg(tmpbuf);
  30.         return FALSE;
  31.     }
  32.  
  33.     CommTimeOuts.ReadIntervalTimeout = 0xffffffff ; /* Indefinite */
  34.     CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
  35.     CommTimeOuts.ReadTotalTimeoutConstant = 0;
  36.     CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
  37.     CommTimeOuts.WriteTotalTimeoutConstant = 0xffffffff; /* indefinite */
  38.    
  39.     SetCommTimeouts(tp->tt_ttyh, &CommTimeOuts);
  40.     result = TTYSetup(tp);
  41.  
  42.     if (!result) {
  43.        CloseHandle(tp->tt_ttyh) ;
  44.        usermsg("Cannot set up TTY");
  45.        return FALSE;
  46.     }
  47.     SetCommMask(tp->tt_ttyh, EV_RING | EV_RXCHAR | EV_DSR | EV_RLSD | EV_BREAK);
  48.     SetupComm(tp->tt_ttyh, 4096, 4096);
  49.     
  50.     if ((tp->tt_RdEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) == INVALID_HANDLE_VALUE) {
  51.         CloseHandle(tp->tt_ttyh);
  52.         usermsg("Cannot Create TTY RdEvent");
  53.         return FALSE;
  54.     }    
  55.     memset (&tp->tt_RdOverlap, 0, sizeof(OVERLAPPED));
  56.     tp->tt_RdOverlap.hEvent = tp->tt_RdEvent;
  57.  
  58.     if ((tp->tt_WrEvent = CreateEvent(NULL,TRUE,FALSE,NULL)) == INVALID_HANDLE_VALUE) {
  59.         CloseHandle(tp->tt_ttyh);
  60.         usermsg("Cannot Create TTY WrEvent");
  61.         return FALSE;
  62.     }    
  63.     memset (&tp->tt_WrOverlap, 0, sizeof(OVERLAPPED));
  64.     tp->tt_WrOverlap.hEvent = tp->tt_WrEvent;
  65.     
  66.     /*
  67.      * If no hardware handshaking desired, raise DTR and RTS
  68.      */
  69.  
  70.     if (!(tp->tt_flags & TTY_DSRDTR))
  71.         EscapeCommFunction( tp->tt_ttyh, SETDTR);
  72.         
  73.     Sleep(200);
  74.     
  75.     if (!(tp->tt_flags & TTY_RTSCTS))
  76.         EscapeCommFunction( tp->tt_ttyh, SETRTS);
  77.  
  78.     return TRUE;
  79. }
  80.  
  81. static BOOL
  82. TTYSetup ( tty_t *tp )
  83. {
  84.     DCB        dcb ;
  85.     
  86.     dcb.DCBlength = sizeof(DCB);
  87.     GetCommState(tp->tt_ttyh, &dcb) ;
  88.     
  89.     dcb.BaudRate = tp->tt_baudrate ;
  90.     dcb.ByteSize = tp->tt_databits ;
  91.     dcb.Parity = tp->tt_parity ;
  92.     dcb.StopBits = tp->tt_stopbits ;
  93.  
  94.     if (dcb.fOutxDsrFlow = ((tp->tt_flags & TTY_DSRDTR) != 0))
  95.        dcb.fDtrControl = DTR_CONTROL_HANDSHAKE ;
  96.     else
  97.        dcb.fDtrControl = DTR_CONTROL_ENABLE ;
  98.        
  99.     dcb.fDsrSensitivity = 0;
  100.     
  101.     if (dcb.fOutxCtsFlow = ((tp->tt_flags & TTY_RTSCTS) != 0)) 
  102.        dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ;
  103.     else
  104.        dcb.fRtsControl = RTS_CONTROL_ENABLE ;
  105.  
  106.     dcb.fInX = dcb.fOutX = (BYTE) ((tp->tt_flags & TTY_XONXOFF) != 0);
  107.     dcb.fTXContinueOnXoff = TRUE;
  108.     dcb.fNull = TRUE;
  109.     dcb.fAbortOnError = FALSE;
  110.     dcb.XonChar = (char )17;   /* ASCII XON */
  111.     dcb.XoffChar = (char )19;  /* ASCII XOFF */
  112.     dcb.XonLim = 100;
  113.     dcb.XoffLim = 100;
  114.  
  115.     dcb.fBinary = TRUE ;
  116.     dcb.fParity = (tp->tt_parity != NOPARITY);
  117.  
  118.     return SetCommState( tp->tt_ttyh, &dcb ) ;
  119. }
  120.  
  121. static BOOL
  122. TTYClose(tty_t *tp)
  123. {
  124.     BOOL status;
  125.     
  126.     /* Stop receiving event notifications */
  127.  
  128.     if (!tp->tt_ttyh)
  129.         return TRUE;
  130.  
  131.     SetCommMask( tp->tt_ttyh, 0);
  132.  
  133.     /* Drop RTS */
  134.     EscapeCommFunction( tp->tt_ttyh, CLRRTS);
  135.     Sleep(100);
  136.  
  137.     /* Drop DTR */
  138.     EscapeCommFunction( tp->tt_ttyh, CLRDTR);
  139.     Sleep(100);
  140.  
  141.     /* Purge all data in all buffers */
  142.  
  143.     PurgeComm( tp->tt_ttyh, PURGE_TXABORT | PURGE_RXABORT |
  144.                            PURGE_TXCLEAR | PURGE_RXCLEAR );
  145.  
  146.     /* Release resources */
  147.  
  148.     if (status = CloseHandle(tp->tt_ttyh)) {
  149.         tp->tt_ttyh = NULL;
  150.         CloseHandle(tp->tt_RdEvent);
  151.         DeleteObject(tp->tt_RdEvent);
  152.         CloseHandle(tp->tt_WrEvent);
  153.         DeleteObject(tp->tt_WrEvent);
  154.     }    
  155.     return status;
  156. }
  157.  
  158. /*
  159.  * Read up to maxlen chars into buf. return 0 if nothing available,
  160.  * less than zero if an error, or the number of chars received and read.
  161.  */
  162.  
  163. static int
  164. TTYRead(tty_t *tp, char * buf, int maxlen, int *error )
  165. {
  166.     BOOL    status;
  167.     COMSTAT ComStatus;
  168.     DWORD   errors, len;
  169.     int     lerror;
  170.  
  171.     *error = 0;
  172.     ClearCommError(tp->tt_ttyh, &errors, &ComStatus ) ;
  173.  
  174.     if (errors > 0) {
  175.        *error = (int) errors;
  176.        return -1;
  177.     }
  178.  
  179.     len = min((int)maxlen, (int)ComStatus.cbInQue);
  180.  
  181.     if (len > 0) {
  182.        status = ReadFile( tp->tt_ttyh, buf, len, &len, &tp->tt_RdOverlap) ;
  183.        if (!status) {
  184.           if ((lerror = GetLastError()) == ERROR_IO_PENDING) {
  185.              if (WaitForSingleObject( tp->tt_RdOverlap.hEvent, 20000)) {
  186.                 *error = GetLastError();
  187.                 return -1;
  188.              }
  189.              else {
  190.                 GetOverlappedResult( tp->tt_ttyh, &tp->tt_RdOverlap, &len, FALSE ) ;
  191.                 tp->tt_RdOverlap.Offset += len;
  192.              }
  193.           }
  194.           else {
  195.             printf("\n<Line error %08x>\n", lerror);
  196.             *error = lerror;
  197.             return -1;
  198.           }
  199.        }
  200.     }
  201.     return len;
  202. }
  203.  
  204. /*
  205.  * Write a character to tty port. (Should really return status...)
  206. */
  207.  
  208. static void
  209. TTYWriteChar(tty_t *tp, char byte)
  210. {
  211.     int     status, error;
  212.     DWORD   written;
  213.  
  214.     status = WriteFile( tp->tt_ttyh, (LPSTR) &byte, 1,
  215.                             &written, &tp->tt_WrOverlap);
  216.  
  217.     if (!status) {
  218.         if ((error = GetLastError()) == ERROR_IO_PENDING) {
  219.             status = WaitForSingleObject( tp->tt_WrOverlap.hEvent, 10000);
  220.             if ( status == WAIT_OBJECT_0 ) {
  221.                 GetOverlappedResult( tp->tt_ttyh, &tp->tt_WrOverlap, &written, FALSE );
  222.                 tp->tt_WrOverlap.Offset += written;
  223.             }
  224.             else {
  225.                 /* Other Error occured (including deadlock or even an actual
  226.                  * timeout caused by other load). */ ;
  227.             }
  228.         }
  229.         else {
  230.             /* Other I/O error occured. System could not initiate the write */ ;
  231.         }
  232.     }
  233. }
  234.  
  235. /*
  236.  * Write an array of characters to tty port
  237.  */
  238.  
  239. static void
  240. TTYWrite(tty_t *tp, char *bufp, int buflen)
  241. {
  242.     for (; buflen > 0; buflen--)
  243.         TTYWriteChar(tp, *bufp++);
  244. }
  245.  
  246.  
  247. /*
  248.  *****************************************************************************
  249.  *              *** ALL CODE BELOW IS JUST A SAMPLE ***                      *
  250.  *                                                                           *
  251.  * SAMPLE ROUTINE -- THIS ONE IS SIMPLY A (VERY) DUMB TERMINAL EMULATOR.     *
  252.  * IT IS HERE TO SHOW THAT WE CAN ACTUALLY DO COMx READS and WRITES          *
  253.  *****************************************************************************
  254.  */
  255.  
  256. static HANDLE hConOutput, hConInput, rthread, wthread;
  257. static DWORD oldmode;
  258.  
  259. HANDLE getwt(void) /* see comments below about compiler optimization problems */
  260. {
  261.     return wthread;
  262. }
  263.  
  264. HANDLE getrt(void)
  265. {
  266.     return rthread;
  267. }
  268.  
  269. BOOL
  270. TTYProc(tp)
  271. tty_t *tp;
  272. {
  273.     DWORD dummy, mode;
  274.     extern void Reader(tty_t *);
  275.     extern void Writer(tty_t *);
  276.     
  277.     if (wthread || rthread) {
  278.         return TRUE;
  279.     }
  280.     
  281.     if (!TTYOpen(tp))
  282.         return FALSE;
  283.         
  284.     Sleep(100);
  285.     /*
  286.      * Set Keyboard input to uncooked mode (no line delimiter, no echo)
  287.      */
  288.          
  289.     hConInput = GetStdHandle(STD_INPUT_HANDLE);
  290.     hConOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  291.  
  292.     if (!hConInput || !hConOutput)
  293.         return FALSE;
  294.  
  295.     if (GetConsoleMode(hConInput, &oldmode) == FALSE) {
  296.         CloseHandle(hConInput);
  297.         CloseHandle(hConOutput);
  298.         return FALSE;
  299.     }
  300.  
  301.     mode = oldmode; /* Keep a copy for later restoration */
  302.     mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
  303.  
  304.     if (SetConsoleMode(hConInput, mode) == FALSE) {
  305.         CloseHandle(hConInput);
  306.         CloseHandle(hConOutput);
  307.         return FALSE;
  308.     }
  309.     
  310.     /*
  311.      * Create auxiliary thread to read the COM port and write to screen
  312.      */
  313.     wthread = CreateThread ((LPSECURITY_ATTRIBUTES)NULL,  (DWORD)0,
  314.         (LPTHREAD_START_ROUTINE)Writer, (LPVOID)tp,
  315.         (DWORD)0, (LPDWORD)&dummy);
  316.  
  317.     if (wthread == INVALID_HANDLE_VALUE) {
  318.         CloseHandle(hConInput);
  319.         CloseHandle(hConOutput);
  320.         return FALSE;
  321.     }
  322.  
  323.     rthread = CreateThread ((LPSECURITY_ATTRIBUTES)NULL,  (DWORD)0,
  324.         (LPTHREAD_START_ROUTINE)Reader, (LPVOID)tp,
  325.         (DWORD)0, (LPDWORD)&dummy);
  326.  
  327.     if (rthread == INVALID_HANDLE_VALUE) {
  328.         CloseHandle(hConInput);
  329.         CloseHandle(hConOutput);
  330.         return FALSE;
  331.     }
  332.     return TRUE;
  333. }
  334.  
  335. static void
  336. Writer(tp)
  337. tty_t *tp;
  338. {
  339.     int rd;
  340.     char c;
  341.  
  342.     diag("Begin Writer Thread\n");
  343.     Sleep(100);
  344.     /*
  345.      * Begin main thread. Read keyboard and write to the COM port.
  346.      */
  347.      
  348.     for(;;) {
  349.         if (ReadFile(hConInput, &c, 1, &rd, NULL) == FALSE)
  350.             break;
  351.  
  352.         if (rd > 0)
  353.             TTYWriteChar(tp, c);
  354.  
  355.         if (!getrt()) {
  356.             break;
  357.         }
  358.     }
  359.     
  360.     if (getrt()) {
  361.         (void) TerminateThread(rthread, 0);
  362.         rthread = NULL;
  363.     }
  364.     
  365.     TTYClose(tp);
  366.         
  367.     (void) SetConsoleMode(hConInput, oldmode); /* if we can't set , tuff */
  368.     CloseHandle(hConInput);
  369.     CloseHandle(hConOutput);
  370.     
  371.     wthread = NULL;
  372.     Sleep(0);
  373.     ExitThread(0);
  374. }    
  375.  
  376. static void
  377. Reader(tty_t *tp)
  378. {
  379.     int rd, error, nwr, status;
  380.     int EventMask;
  381.     char buf[256];
  382.  
  383.     Sleep(400);
  384.     diag("Begin Reader thread.\n");
  385.         
  386.     for (;;) {
  387.         EventMask = 0;
  388.                 
  389. #ifdef TOTALLYASYNCHRONOUS
  390.         /* Wait for 10 seconds for something to happen */
  391.  
  392.         status = WaitCommEvent(tp->tt_ttyh, &EventMask, &tp->tt_RdOverlap);
  393.  
  394.         if (!status) {
  395.             error = GetLastError();
  396.             if ((unsigned)error == ERROR_IO_PENDING)
  397.                 WaitForSingleObject(tp->tt_RdEvent, 10000);
  398.             else {
  399.                 break;
  400.             }
  401.         }
  402. #else
  403.         /*
  404.          * Wait indefinitely (barring a ^C) for something to
  405.          * happen on the COM port.
  406.          */
  407.          
  408.         (void) WaitCommEvent(tp->tt_ttyh, &EventMask, NULL);
  409. #endif
  410.         
  411.         if (EventMask & (EV_DSR| EV_RLSD | EV_BREAK | EV_RING)) {
  412.             GetCommModemStatus(tp->tt_ttyh, &status);
  413.             if (EventMask & EV_DSR)
  414.                 diag("\n<DSR %sASSERTED>\n", (status & MS_DSR_ON) ? "" : "DE");
  415.             if (EventMask & EV_RLSD);
  416.                 diag("\n<CARRIER DETECT %sASSERTED>\n", (status & MS_RLSD_ON) ? "" : "DE");
  417.             if (EventMask & EV_BREAK)
  418.                 diag("\n<LINE BREAK>\n");
  419.             if ((EventMask & EV_RING) && (status & MS_RING_ON))
  420.                 diag("\n<RRRRRING!>\n");
  421.         }
  422.         
  423.         /* Now attend to the business of reading the port and echoing */
  424.         
  425.         if (EventMask & EV_RXCHAR) {
  426.             do {
  427.                 rd = TTYRead(tp, buf, sizeof buf, &error);
  428.                 if (rd > 0)
  429.                     WriteFile(hConOutput, buf, rd, &nwr, NULL);
  430.                 if (!getwt()) /* see comment below */
  431.                     break;
  432.             } while(rd > 0);
  433.         }
  434.     }
  435.     
  436.     /*
  437.      * If "too high" a level of optimization is used when compiling, this
  438.      * thread may not be able to discover that the value of the
  439.      * handle "wthread" has changed (i.e., variable kept in register).
  440.      * To avoid it, we simply call an external function to return the
  441.      * value instead of simply using it here. Hence the call to getwt().
  442.      */
  443.  
  444.     if (getwt()) {
  445.         (void) TerminateThread(wthread, 0);
  446.         wthread = NULL;
  447.     }
  448.     
  449.     TTYClose(tp);
  450.  
  451.     (void) SetConsoleMode(hConInput, oldmode); /* if we can't set , tuff */
  452.     CloseHandle(hConOutput);
  453.     CloseHandle(hConInput);
  454.     rthread = NULL; /* Signal main thread we're exiting */
  455.     Sleep(0);
  456.     ExitThread(0);
  457. }
  458.